﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.ApplicationModel.Resources.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using System.Collections.Specialized;
using Windows.Web.Syndication;
using System.Threading.Tasks;
using Windows.System.Threading;
using Windows_Azure_App.DataModel;
using System.Text;

// The data model defined by this file serves as a representative example of a strongly-typed
// model that supports notification when members are added, removed, or modified.  The property
// names chosen coincide with data bindings in the standard item templates.
//
// Applications may use this model as a starting point and build on it, or discard it entirely and
// replace it with something appropriate to their needs.

namespace Windows_Azure_App.Data
{
    /// <summary>
    /// Base class for <see cref="AzureServiceStatusItem"/> and <see cref="AzureServiceRegionGroup"/> that
    /// defines properties common to both.
    /// </summary>
    [Windows.Foundation.Metadata.WebHostHidden]
    public abstract class DataCommon : Windows_Azure_App.Common.BindableBase
    {
        private static Uri _baseUri = new Uri("ms-appx:///");

        public DataCommon(String uniqueId, String title, String subtitle, String imagePath, String description)
        {
            this._uniqueId = uniqueId;
            this._title = title;
            this._subtitle = subtitle;
            this._description = description;
            this._imagePath = imagePath;
        }

        private string _uniqueId = string.Empty;
        public string UniqueId
        {
            get { return this._uniqueId; }
            set { this.SetProperty(ref this._uniqueId, value); }
        }

        private string _title = string.Empty;
        public string Title
        {
            get { return this._title; }
            set { this.SetProperty(ref this._title, value); }
        }

        private string _subtitle = string.Empty;
        public string Subtitle
        {
            get { return this._subtitle; }
            set { this.SetProperty(ref this._subtitle, value); }
        }

        private string _description = string.Empty;
        public string Description
        {
            get { return this._description; }
            set { this.SetProperty(ref this._description, value); }
        }

        private ImageSource _image = null;
        private String _imagePath = null;
        public ImageSource Image
        {
            get
            {
                if (this._image == null && this._imagePath != null)
                {
                    this._image = new BitmapImage(new Uri(DataCommon._baseUri, this._imagePath));
                }
                return this._image;
            }

            set
            {
                this._imagePath = null;
                this.SetProperty(ref this._image, value);
            }
        }

        public void SetImage(String path)
        {
            this._image = null;
            this._imagePath = path;
            this.OnPropertyChanged("Image");
        }

        public override string ToString()
        {
            return this.Title;
        }
    }

    /// <summary>
    /// Generic item data model.
    /// </summary>
    public class AzureServiceStatusItem : DataCommon
    {

        public AzureServiceStatusItem(String uniqueId, String title, String subtitle, String imagePath, String description, String content, String status, String rssFeed, AzureServiceRegionGroup group)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            this._content = content;
            this._group = group;
            this._status = status;
            this._rssfeedpath = rssFeed;
        }

        private string _status = string.Empty;
        public string Status {
            get { return this._status; }
            set { this.SetProperty(ref this._status, value); }
        }

        private DateTime _publishedDate;
        public DateTime PublishedDate { 
            get { return this._publishedDate; }
            set { this.SetProperty(ref this._publishedDate, value); }
        }

        private string _content = string.Empty;
        public string Content
        {
            get {
                StringBuilder sb = new StringBuilder();
                foreach (var item in _rssFeedItems)
                {
                    sb.AppendLine(item.Title);
                    sb.AppendLine(item.Subtitle);
                    sb.AppendLine(item.Description);
                    sb.AppendLine("");
                }
                return sb.ToString();
  
                //return this._content;
            }
            set { this.SetProperty(ref this._content, value); }
        }

        //todo: insert RssFeed object
        private string _rssfeedpath = string.Empty;
        public string RssFeedPath {
            get { return this._rssfeedpath; }
            set { this.SetProperty(ref this._rssfeedpath, value); }
        }

        private AzureServiceRegionGroup _group;
        public AzureServiceRegionGroup Group
        {
            get { return this._group; }
            set { this.SetProperty(ref this._group, value); }
        }
        /// <summary>
        /// To be loaded right before the content needs to be shown to save time
        /// </summary>
        private ObservableCollection<AzureServiceRssFeed> _rssFeedItems = new ObservableCollection<AzureServiceRssFeed>();
        public ObservableCollection<AzureServiceRssFeed> RssFeedItems
        {
            get { return this._rssFeedItems; }
        }
    }

    public class AzureServiceRssFeed : DataCommon
    {
        public AzureServiceRssFeed(String uniqueId, String title, String subtitle, String imagePath, String description, DateTime publishedDate, AzureServiceStatusItem statusitem)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            this._publishedDate = publishedDate;
            this._group = statusitem;
        }
        private DateTime _publishedDate;
        public DateTime PublishedDate {
            get { return this._publishedDate; }
            set { this.SetProperty(ref this._publishedDate, value); }
        }

        private AzureServiceStatusItem _group;
        public AzureServiceStatusItem Group
        {
            get { return this._group; }
        }

    }

    /// <summary>
    /// Generic group data model.
    /// </summary>
    public class AzureServiceRegionGroup : DataCommon
    {

        public AzureServiceRegionGroup(String uniqueId, String title, String subtitle, String imagePath, String description)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            Items.CollectionChanged += ItemsCollectionChanged;
        }

        private void ItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // Provides a subset of the full items collection to bind to from a GroupedItemsPage
            // for two reasons: GridView will not virtualize large items collections, and it
            // improves the user experience when browsing through groups with large numbers of
            // items.
            //
            // A maximum of 12 items are displayed because it results in filled grid columns
            // whether there are 1, 2, 3, 4, or 6 rows displayed

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (e.NewStartingIndex < 12)
                    {
                        TopItems.Insert(e.NewStartingIndex,Items[e.NewStartingIndex]);
                        if (TopItems.Count > 12)
                        {
                            TopItems.RemoveAt(12);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    if (e.OldStartingIndex < 12 && e.NewStartingIndex < 12)
                    {
                        TopItems.Move(e.OldStartingIndex, e.NewStartingIndex);
                    }
                    else if (e.OldStartingIndex < 12)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        TopItems.Add(Items[11]);
                    }
                    else if (e.NewStartingIndex < 12)
                    {
                        TopItems.Insert(e.NewStartingIndex, Items[e.NewStartingIndex]);
                        TopItems.RemoveAt(12);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    if (e.OldStartingIndex < 12)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        if (Items.Count >= 12)
                        {
                            TopItems.Add(Items[11]);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    if (e.OldStartingIndex < 12)
                    {
                        TopItems[e.OldStartingIndex] = Items[e.OldStartingIndex];
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    TopItems.Clear();
                    while (TopItems.Count < Items.Count && TopItems.Count < 12)
                    {
                        TopItems.Add(Items[TopItems.Count]);
                    }
                    break;
            }
        }

        private ObservableCollection<AzureServiceStatusItem> _items = new ObservableCollection<AzureServiceStatusItem>();
        public ObservableCollection<AzureServiceStatusItem> Items
        {
            get { return this._items; }
        }

        private ObservableCollection<AzureServiceStatusItem> _topItem = new ObservableCollection<AzureServiceStatusItem>();
        public ObservableCollection<AzureServiceStatusItem> TopItems
        {
            get {return this._topItem; }
        }
    }

    /// <summary>
    /// Creates a collection of groups and items which are scraped from www.windowsAzurestatus.com.
    /// The collections are used to display the current status of Windows Azure in all regions
    /// @TODO
    /// 1. images per region
    /// 2. Caching of RSSFeeds
    /// 3. Caching the website
    /// 4. Options to choose services and regions
    /// 5. Grouping per service rather than region 
    /// </summary>
    public sealed class AzureServiceRSSFeedDataSource
    {
        private static AzureServiceRSSFeedDataSource _dataSource = new AzureServiceRSSFeedDataSource();

        private ObservableCollection<AzureServiceRegionGroup> _allGroups = new ObservableCollection<AzureServiceRegionGroup>();
        public ObservableCollection<AzureServiceRegionGroup> AllGroups
        {
            get { return this._allGroups; }
        }

        public static IEnumerable<AzureServiceRegionGroup> GetGroups(string uniqueId)
        {
            if (!uniqueId.Equals("AllGroups")) throw new ArgumentException("Only 'AllGroups' is supported as a collection of groups");
            return _dataSource.AllGroups;
        }

        public static AzureServiceRegionGroup GetGroup(string uniqueId)
        {
            // Simple linear search is acceptable for small data sets
            var matches = _dataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
            if (matches.Count() == 1) return matches.First();
            return null;
        }

        public static AzureServiceStatusItem GetItem(string uniqueId)
        {
            // Simple linear search is acceptable for small data sets
            AzureServiceStatusItem item = _dataSource.AllGroups
                .SelectMany(group => group.Items)
                    .Where((i) => i.UniqueId.Equals(uniqueId))
                        .First();
            if ((item.RssFeedItems.Count > 0) == false)
            {
                LoadRssFeedInStatusItem(item);    
            }
            return item;
        }

        private static async void LoadRssFeedInStatusItem(AzureServiceStatusItem item)
        {
            string feedUrl = item.RssFeedPath;
            StringBuilder uniqueIdBuilder = new StringBuilder();
            List<AzureServiceWebsiteObject> feedObjects = await RSSFeedSourceHelper.LoadRssFeedInStatusItem(feedUrl);
            foreach (AzureServiceWebsiteObject feedObject in feedObjects)
            {
                uniqueIdBuilder.Clear();
                uniqueIdBuilder.AppendFormat("{0} {1}", feedObject.Title, feedObject.PublishedDate.ToString());
                string uniqueId = uniqueIdBuilder.ToString();
                AzureServiceRssFeed feed = new AzureServiceRssFeed(
                    uniqueId, feedObject.Title, item.Title, feedObject.ImagePath, feedObject.Description, 
                    feedObject.PublishedDate, item);
                item.RssFeedItems.Add(feed);
            }

        }

        internal static AzureServiceRssFeed GetRssFeedItem(string uniqueId)
        {
            AzureServiceRssFeed feed = _dataSource.AllGroups
                .SelectMany((group) => group.Items)
                .SelectMany(i => i.RssFeedItems)
                .Where(rss => rss.UniqueId.Equals(uniqueId))
                        .First();
            return feed;
        }

        #region RSS feed methods
        

        
        #endregion

        public AzureServiceRSSFeedDataSource()
        {
            RetrieveData();
        }

        public async Task RetrieveData()
        {
            //List<string> feedUrls = await RSSFeedSourceHelper.AllRssFeeds();
            List<string> groups = await RSSFeedSourceHelper.GetRegions();
            List<AzureServiceWebsiteObject> statusobjectlist =
                await RSSFeedSourceHelper.GetScrapedStatusWindowsAzureStatusCom();

            foreach (string g in groups)
            {
                AzureServiceRegionGroup group = new AzureServiceRegionGroup(
                 g, g, g, "Assets/DarkGrey.png", "This is specific region in which a datacenter of Windows Azure is located");
                List<AzureServiceWebsiteObject> templist = statusobjectlist.Where(o => o.Region.Equals(group.Title)).ToList();
                foreach (AzureServiceWebsiteObject item in templist)
                {
                    string uniqueId = new StringBuilder().AppendFormat("{0} : {1}", item.Title, item.Region).ToString();
                    AzureServiceStatusItem statusItem = new AzureServiceStatusItem(
                        uniqueId, item.Title, item.Region, item.ImagePath, "Description", "Content", item.Status, item.RSSFeed, group);
                    group.Items.Add(statusItem);
                }
                this.AllGroups.Add(group);
            }
        }

        
    }
}
